package com.changhong.framework.sdfs.namenode.server.nio;

import com.changhong.framework.sdfs.namenode.server.builder.NameNodeBuilder;
import com.changhong.framework.sdfs.namenode.server.context.NameNodeContext;

import java.util.concurrent.atomic.AtomicLong;

/**
 * @author summer
 */
public class FileOperationManager {

    private final DoubleFlushManager doubleFlushManager;
    private final ThreadLocal<Long> localId=new ThreadLocal<>();
    private final NameNodeContext nameNodeContext;


    public FileOperationManager(){
        this.nameNodeContext=NameNodeBuilder.getInstance(NameNodeContext.class);
        this.doubleFlushManager=NameNodeBuilder.getInstance(DoubleFlushManager.class);
    }

    private AtomicLong atomicLong=new AtomicLong(0);
    private volatile Boolean isSchedulingSync=false;
    private volatile Boolean isSyncRunning=false;



    public void firstLock(String operationContent)throws Exception{
        //第一段枷锁
        synchronized (this) {
            waitSchedulingSync();
            long operationId = atomicLong.incrementAndGet();
            //保存最大的刷盘id到上下文中去
            nameNodeContext.refreshMaxId(operationId);
            localId.set(operationId);
            EditLog editLog = EditLog.newBuilder().setContent(operationContent).settxId(operationId)
                    .build();
            doubleFlushManager.writeLog(editLog);

            //判断当前是不是已经达到最大的限制了
            if (!doubleFlushManager.shouldSyncToDisk()) {
                return;
            }
            //此时到这里的时候就已经要准备刷盘了，把等待标致打开，让线程卡住，不让写入缓存中
            isSchedulingSync = true;
        }
        //第二段枷锁
        secondLock();
        // 第三段枷锁
        synchronized (this) {
            // 同步完了磁盘之后，就会将标志位复位，再释放锁
            isSyncRunning = false;
            // 唤醒可能正在等待他同步完磁盘的线程
            notifyAll();
        }
    }

    public void secondLock() throws Exception{
        // 第二段枷锁
        synchronized (this) {
            // 如果说当前正好有人在刷内存缓冲到磁盘中去就会进入到当前的判断
            if (isSyncRunning) {
                // 有人已经在刷盘了，需要等待别人先刷完，一直卡着
                while (isSyncRunning) {
                    wait(1000);
                }
            }
            // 交换两块缓冲区
            doubleFlushManager.setReadyToSync();
            isSchedulingSync = false;
            notifyAll(); // 唤醒那些还卡在while循环那儿的线程
            isSyncRunning = true;
        }
        // 开始同步内存缓冲的数据到磁盘文件里去
        // 这个过程其实是比较慢，基本上肯定是毫秒级了，弄不好就要几十毫秒
        doubleFlushManager.flush();
    }

    /**
     * 获取双缓冲管理对象
     * @return 获取双缓冲管理对象
     */
    public DoubleFlushManager getDoubleFlushManager(){
        synchronized (this) {
            return doubleFlushManager;
        }
    }


    public void operation(String operationContent) throws Exception {
        //第一段枷锁
        firstLock(operationContent);
    }

    /**
     * 等待正在调度的刷磁盘的操作
     */
    private void waitSchedulingSync(){
        try {
            while(isSchedulingSync) {
                wait(1000);
            }
        } catch (Exception e) {

        }
    }
}
